/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.web.servlet;


import javax.el.ELContext;
import javax.el.ELResolver;
import java.beans.FeatureDescriptor;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;

/**
 * EL 作用于属性解析器
 *
 * @author vacoor
 */
@SuppressWarnings({"unchecked", "rawtypes"})
class ScopedAttributeELResolver extends ELResolver {
    public static final int CONTEXT_SCOPE = WebRequestContext.CONTEXT_SCOPE;
    public static final int REQUEST_SCOPE = WebRequestContext.REQUEST_SCOPE;
    public static final int SESSION_SCOPE = WebRequestContext.SESSION_SCOPE;
    public static final int APPLICATION_SCOPE = WebRequestContext.APPLICATION_SCOPE;

    public Object getValue(ELContext context, Object base, Object property) {
        if (null == context) {
            throw new NullPointerException();
        }
        if (null == base) {
            context.setPropertyResolved(true);
            if (property instanceof String) {
                return getContext(context).findAttribute((String) property);
            }
        }
        return null;
    }

    public Class<Object> getType(ELContext context, Object base, Object property) {
        if (null == context) {
            throw new NullPointerException();
        }
        if (null == base) {
            context.setPropertyResolved(true);
            return Object.class;
        }
        return null;
    }

    public void setValue(ELContext context, Object base, Object property, Object val) {
        if (null == context) {
            throw new NullPointerException();
        }
        if (null == base) {
            context.setPropertyResolved(true);
            if (property instanceof String) {
                String attr = (String) property;
                WebRequestContext ctx = getContext(context);

                if (ctx.getAttribute(attr, REQUEST_SCOPE) != null) {
                    ctx.setAttribute(attr, val, REQUEST_SCOPE);
                } else if (ctx.getAttribute(attr, SESSION_SCOPE) != null) {
                    ctx.setAttribute(attr, val, SESSION_SCOPE);
                } else if (ctx.getAttribute(attr, APPLICATION_SCOPE) != null) {
                    ctx.setAttribute(attr, val, APPLICATION_SCOPE);
                } else {
                    ctx.setAttribute(attr, val, CONTEXT_SCOPE);
                }
            }
        }
    }

    private WebRequestContext getContext(ELContext context) {
        return (WebRequestContext) context.getContext(WebRequestContext.class);
    }

    @Override
    public boolean isReadOnly(ELContext context, Object base, Object property) {
        if (context == null) {
            throw new NullPointerException();
        }
        if (base == null) {
            context.setPropertyResolved(true);
        }
        return false;
    }

    @Override
    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
        Enumeration attrs;
        ArrayList<FeatureDescriptor> list = new ArrayList<FeatureDescriptor>();
        // WebRequestContext ctx = (WebRequestContext) context.getContext(JspContext.class);
        WebRequestContext ctx = (WebRequestContext) context.getContext(WebRequestContext.class);

        attrs = ctx.getAttributeNamesInScope(CONTEXT_SCOPE);
        while (attrs.hasMoreElements()) {
            String name = (String) attrs.nextElement();
            Object value = ctx.getAttribute(name, CONTEXT_SCOPE);
            FeatureDescriptor descriptor = new FeatureDescriptor();
            descriptor.setName(name);
            descriptor.setDisplayName(name);
            descriptor.setShortDescription("context scope attribute");
            descriptor.setExpert(false);
            descriptor.setHidden(false);
            descriptor.setPreferred(true);
            descriptor.setValue("type", value.getClass());
            descriptor.setValue("resolvableAtDesignTime", Boolean.TRUE);
            list.add(descriptor);
        }

        attrs = ctx.getAttributeNamesInScope(REQUEST_SCOPE);
        while (attrs.hasMoreElements()) {
            String name = (String) attrs.nextElement();
            Object value = ctx.getAttribute(name, REQUEST_SCOPE);
            FeatureDescriptor descriptor = new FeatureDescriptor();
            descriptor.setName(name);
            descriptor.setDisplayName(name);
            descriptor.setShortDescription("request scope attribute");
            descriptor.setExpert(false);
            descriptor.setHidden(false);
            descriptor.setPreferred(true);
            descriptor.setValue("type", value.getClass());
            descriptor.setValue("resolvableAtDesignTime", Boolean.TRUE);
            list.add(descriptor);
        }

        attrs = ctx.getAttributeNamesInScope(SESSION_SCOPE);
        while (attrs.hasMoreElements()) {
            String name = (String) attrs.nextElement();
            Object value = ctx.getAttribute(name, SESSION_SCOPE);
            FeatureDescriptor descriptor = new FeatureDescriptor();
            descriptor.setName(name);
            descriptor.setDisplayName(name);
            descriptor.setShortDescription("session scope attribute");
            descriptor.setExpert(false);
            descriptor.setHidden(false);
            descriptor.setPreferred(true);
            descriptor.setValue("type", value.getClass());
            descriptor.setValue("resolvableAtDesignTime", Boolean.TRUE);
            list.add(descriptor);
        }

        attrs = ctx.getAttributeNamesInScope(APPLICATION_SCOPE);
        while (attrs.hasMoreElements()) {
            String name = (String) attrs.nextElement();
            Object value = ctx.getAttribute(name, APPLICATION_SCOPE);
            FeatureDescriptor descriptor = new FeatureDescriptor();
            descriptor.setName(name);
            descriptor.setDisplayName(name);
            descriptor.setShortDescription("application scope attribute");
            descriptor.setExpert(false);
            descriptor.setHidden(false);
            descriptor.setPreferred(true);
            descriptor.setValue("type", value.getClass());
            descriptor.setValue("resolvableAtDesignTime", Boolean.TRUE);
            list.add(descriptor);
        }
        return list.iterator();
    }

    @Override
    public Class<String> getCommonPropertyType(ELContext context, Object base) {
        return null == base ? String.class : null;
    }
}
